package com.wimcorp.magic.board;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import com.wimcorp.magic.abilities.Ability;
import com.wimcorp.magic.abilities.ComboAbility;
import com.wimcorp.magic.abilities.trigger.OnPlayerCast;
import com.wimcorp.magic.behaviour.Behaviour;
import com.wimcorp.magic.behaviour.Defender;
import com.wimcorp.magic.behaviour.Haste;
import com.wimcorp.magic.behaviour.SummoningSickness;
import com.wimcorp.magic.behaviour.Tapped;
import com.wimcorp.magic.cards.BasicLand;
import com.wimcorp.magic.cards.Creature;
import com.wimcorp.magic.cards.Enchantment;
import com.wimcorp.magic.cards.PlayingCard;
import com.wimcorp.magic.color.Color;
import com.wimcorp.magic.players.Player;
import com.wimcorp.magic.subtypes.SubType;

public class SummoningPlains extends CardAccessor {

	// private static Set<Observer> enterBattlefieldObservers = new
	// HashSet<Observer>();

	private Player player;
	private HashMap<Integer, Map<Long, PlayingCard>> summoningPlains = new HashMap<Integer, Map<Long, PlayingCard>>();

	private HashMap<Creature, Set<Creature>> blockers = new LinkedHashMap<Creature, Set<Creature>>();
	private HashMap<Player, Set<Creature>> attackers = new LinkedHashMap<Player, Set<Creature>>();

	private Graveyard graveyard;
	private Exile exile;
	private Sacrifice sacrifice;

	public SummoningPlains(Player player) {
		this.player = player;
		graveyard = new Graveyard(player);
		exile = new Exile(player);
		sacrifice = new Sacrifice(player);
		for (CardType cardType : CardType.values()) {
			summoningPlains.put(cardType.ordinal(), new HashMap<Long, PlayingCard>());
		}
	}

	public Collection<PlayingCard> getCards(CardType cardType) {
		return summoningPlains.get(cardType.ordinal()).values();
	}

	public PlayingCard getCard(CardType cardType, long identifier) {
		PlayingCard result = summoningPlains.get(cardType.ordinal()).get(identifier);
		if (result == null) {
			// check if creature is controlled by opponent.
			result = (Creature) player.getOpponent().getSummoningPlaines().getCard(CardType.ALL, identifier);
			if (!player.getName().equals(result.getPlayer().getName())) {
				return null;
			}
		}
		return result;
	}

	public Set<Creature> getAttackersOnPlayer(Player player) {
		return attackers.get(player);
	}

	private void addCard(CardType cardType, PlayingCard card) {
		/*
		 * if(card.hasTrigger(OnEnterBattlefield.instance())){ Set<Ability>
		 * abilities = card.getAbilities(OnEnterBattlefield.instance());
		 * for(Ability ability : abilities){ ability.cast(card.getPlayer()); } }
		 */

		summoningPlains.get(cardType.ordinal()).put(card.getIdentifier(), card);
	}

	@Override
	public PlayingCard getCard(long identifier) {
		return getCard(CardType.ALL, identifier);
	}

	public Ability getAbility(long abilityId) {
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			Set<Ability> abilities = card.getAbilities();
			for (Ability ability : abilities) {
				if (ability.getTrigger() instanceof OnPlayerCast && ability.getIdentifier() == abilityId) {
					return ability;
				}
				if (ability instanceof ComboAbility && ability.getIdentifier() == abilityId) {
					for (Ability ability2 : ((ComboAbility) ability).getAbilities()) {
						if (ability2.getTrigger() instanceof OnPlayerCast) {
							return ability;
						}
					}
				}
			}
		}
		return null;
	}

	@Override
	public void remove(PlayingCard card) {
		card.unsummon();
	}

	public void playCard(PlayingCard card) {
		addCard(CardType.ALL, card);
		if (card instanceof BasicLand) {
			addCard(CardType.LAND, card);
		}
		card.registerAbilities();
	}

	public void declareAttack(Player targetPlayer, Creature attacker) {
		Set<Creature> aggressors = attackers.get(targetPlayer);
		if (aggressors == null) {
			aggressors = new HashSet<Creature>();
		}
		aggressors.add(attacker);
		attackers.put(targetPlayer, aggressors);
		addCard(CardType.ATTACKER, attacker);

	}

	public void declareBlocker(Creature attacker, Creature blocker) {
		addCard(CardType.BLOCKER, blocker);
		attacker.registerBlocker(blocker);
	}

	public void registerBlocker(Creature attacker, Creature blocker) {
		Set<Creature> defenders = blockers.get(attacker);
		if (defenders == null) {
			defenders = new HashSet<Creature>();
		}
		defenders.add(blocker);
		blockers.put(attacker, defenders);
	}

	public void moveToGraveyard(PlayingCard card) {
		graveyard.add(card);
	}

	public void moveToExile(PlayingCard card) {
		exile.add(card);
	}

	public void unsummon(PlayingCard card) {
		player.getSummoningPlaines().getCards(CardType.ALL).remove(card);
		player.getSummoningPlaines().getCards(CardType.LAND).remove(card);
		player.getSummoningPlaines().getCards(CardType.ATTACKER).remove(card);
		player.getSummoningPlaines().getCards(CardType.BLOCKER).remove(card);

		Player opponent = player.getOpponent();
		opponent.getSummoningPlaines().getCards(CardType.ALL).remove(card);
		opponent.getSummoningPlaines().getCards(CardType.LAND).remove(card);
		opponent.getSummoningPlaines().getCards(CardType.ATTACKER).remove(card);
		opponent.getSummoningPlaines().getCards(CardType.BLOCKER).remove(card);
		// summoningPlains.get(CardType.ALL.ordinal()).remove(card.getIdentifier());
		// summoningPlains.get(CardType.LAND.ordinal()).remove(card.getIdentifier());
		// summoningPlains.get(CardType.ATTACKER.ordinal()).remove(card.getIdentifier());
		// summoningPlains.get(CardType.BLOCKER.ordinal()).remove(card.getIdentifier());
		card.unRegisterAbilities();
		/*if (card instanceof Creature) {
			Creature creature = (Creature) card;
			for (Enchantment enchantment : creature.getEnchantments()) {
				creature.removeEnchantment(enchantment);
			}
		}*/
	}

	public Set<Creature> getBlockers(Creature attacker) {
		return blockers.get(attacker);
	}

	public Graveyard getGraveyard() {
		return graveyard;
	}

	public Exile getExile() {
		return exile;
	}

	public Sacrifice getSacrifice() {
		return sacrifice;
	}

	public Set<Creature> getCreatureOnBattlefield(SubType subType) {
		Set<Creature> creatures = new HashSet<Creature>();
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			if (card instanceof Creature) {
				Creature creature = (Creature) card;
				if (creature.hasSubType(subType)) {
					creatures.add(creature);
				}
			}
		}
		return creatures;
	}

	public Set<Creature> getCreatureOnBattlefieldWithBehavior(Behaviour... behaviours) {
		Set<Creature> creatures = new HashSet<Creature>();
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			if (card instanceof Creature) {
				Creature creature = (Creature) card;
				for (Behaviour behaviour : behaviours) {
					if (creature.hasBehaviour(behaviour)) {
						creatures.add(creature);
					}
				}
			}
		}
		return creatures;
	}

	public Set<Creature> getCreatureOnBattlefieldWithColor(Set<Color> colors) {
		Set<Creature> creatures = new HashSet<Creature>();
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			if (card instanceof Creature) {
				Creature creature = (Creature) card;
				for (Color color : colors) {
					if (creature.getColors().contains(color)) {
						creatures.add(creature);
					}
				}
			}
		}
		return creatures;
	}

	public int getCreatureCountOnBattlefield() {
		int count = 0;
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			if (card instanceof Creature) {
				count++;
			}
		}
		return count;
	}

	public int getAttackeableCreatureCountOnBattlefield() {
		int count = 0;
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			if (card instanceof Creature && !card.hasBehaviour(SummoningSickness.instance())
					&& !card.hasBehaviour(Tapped.instance()) && !card.hasBehaviour(Defender.instance())) {
				if (card.getPlayer().getName().equals(player.getName())) {
					count++;
				}
			} else if (card instanceof Creature && card.hasBehaviour(Haste.instance())
					&& !card.hasBehaviour(Tapped.instance()) && !card.hasBehaviour(Defender.instance())) {
				if (card.getPlayer().getName().equals(player.getName())) {
					count++;
				}
			}
		}
		Collection<PlayingCard> opponentCards = player.getOpponent().getSummoningPlaines().getCards(CardType.ALL);
		for (PlayingCard card : opponentCards) {
			if (card instanceof Creature && !card.hasBehaviour(SummoningSickness.instance())
					&& !card.hasBehaviour(Tapped.instance()) && !card.hasBehaviour(Defender.instance())) {
				if (card.getPlayer().getName().equals(player.getName())) {
					count++;
				}
			} else if (card instanceof Creature && card.hasBehaviour(Haste.instance())
					&& !card.hasBehaviour(Tapped.instance()) && !card.hasBehaviour(Defender.instance())) {
				if (card.getPlayer().getName().equals(player.getName())) {
					count++;
				}
			}
		}
		return count;
	}

	public int getBlockableCreatureCountOnBattlefield() {
		int count = 0;
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			if (card instanceof Creature && !card.hasBehaviour(Tapped.instance())) {
				count++;
			}
		}
		return count;
	}

	public int getCastableAbilitiesOnBatleField() {
		int count = 0;
		Collection<PlayingCard> allCards = getCards(CardType.ALL);
		for (PlayingCard card : allCards) {
			Set<Ability> abilities = card.getAbilities();
			for (Ability ability : abilities) {
				if (ability.getTrigger() instanceof OnPlayerCast) {
					count++;
				}
				if (ability instanceof ComboAbility) {
					for (Ability ability2 : ((ComboAbility) ability).getAbilities()) {
						if (ability2.getTrigger() instanceof OnPlayerCast) {
							count++;
						}
					}
				}
			}
		}
		return count;
	}

	public void resetAttackers() {
		summoningPlains.put(CardType.ATTACKER.ordinal(), new HashMap<Long, PlayingCard>());
		attackers = new LinkedHashMap<Player, Set<Creature>>();
	}

	public void resetBlockers() {
		summoningPlains.put(CardType.BLOCKER.ordinal(), new HashMap<Long, PlayingCard>());
		blockers = new LinkedHashMap<Creature, Set<Creature>>();
	}

	/*
	 * public static void registerEnterBattlefieldObserver(Observer o) {
	 * enterBattlefieldObservers.add(o); }
	 * 
	 * public static void unRegisterEnterBattlefieldObserver(Observer o) {
	 * enterBattlefieldObservers.remove(o); }
	 */

}
